/* 
 * Copyright (C) 2012 UTSChedule
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package utsc.apps.utschedule.ui.activities;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import utsc.apps.utschedule.R;
import utsc.apps.utschedule.classes.Assignment;
import utsc.apps.utschedule.classes.Course;
import utsc.apps.utschedule.classes.Exam;
import utsc.apps.utschedule.ui.InputDialogFragment;
import utsc.apps.utschedule.ui.InputDialogFragment.OnInputListener;
import utsc.apps.utschedule.ui.MessageDialogFragment;
import utsc.apps.utschedule.ui.activities.adapters.SearchResultAdapter;
import utsc.apps.utschedule.ui.interfaces.TimetableActivity;
import utsc.apps.utschedule.util.TimetableHelper;
import utsc.apps.utschedule.util.provider.SearchProvider;
import android.app.ProgressDialog;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.TextView;

import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;

/**
 * This is the in app search interface. It is also the search handler for the
 * global system search as well. It performs two different searches, one is to
 * search everything (course, assignment, and exam) from the local database, and the other one is to search
 * courses from Intranet.
 * 
 */
public class SearchActivity extends GenericFragmentActivity implements LoaderCallbacks<Cursor>, OnInputListener,OnItemClickListener{
	
	private TextView _txtSearchText;
	private ListView _lstResults;
	String _queryString="";
	private SimpleCursorAdapter _resultAdapter;
	private SearchResultAdapter _searchResultAdapter;
	private Cursor _dbResultCursor;
	private ArrayList<Course> _searchResultCursor;
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
		 super.onCreate(savedInstanceState);
        handleIntent(getIntent());
    }

    @Override
    protected void onNewIntent(Intent intent) {
        handleIntent(intent);
    }

    /**
     * The callback to handle the search intent.
     * @param intent the search intent
     */
    private void handleIntent(Intent intent) {
    	String action=intent.getAction();
    	//if the search intent is from a search action
        if (Intent.ACTION_SEARCH.equals(action)) {
			Uri data = intent.getData();
			
			//then prepare the search result
			setContentView(R.layout.activity_search);
			_txtSearchText = (TextView) findViewById(R.id.text);
			_lstResults = (ListView) findViewById(R.id.list);
			_lstResults.setCacheColorHint(Color.TRANSPARENT);
			String query = intent.getStringExtra(SearchManager.QUERY);
			String userQuery=null;
			
			// try to get the query data if it was requested for online search
			Object userQueryData = intent.getExtras().get(SearchManager.USER_QUERY);
	            if (userQueryData instanceof String) {
	            	userQuery=(String) userQueryData;
				}else {
					userQuery=userQueryData.toString();
				}
	           
			if (data != null) {
				List<String> path = data.getPathSegments();
				//if the requested search is for online search
				String type = path.get(0);
				if (type.equals("online")) {
					if (!TextUtils.isEmpty(query)) {
						_queryString = query;
					} else {
						_queryString = userQuery;
					}
					if (TextUtils.isEmpty(_queryString)) {
						if (path.size()>1) {
							_queryString=path.get(1);
						}else {
							_queryString="";
						}
					}
					showTermDialog();
				}
			} else {
				showResults(query);
			}
			_lstResults.setOnItemClickListener(this);

        }
        //if the requested action is for view
        //show the associated detail view of the data
        else if (Intent.ACTION_VIEW.equals(action)) {
        	Uri data=intent.getData();
        	gotoSearchView(data);
		}
    }
    
    /**
     * Analyze the search intent data, then go to the associated detail view.
     * @param data the search intent data
     */
    private void gotoSearchView(Uri data) {
    	List<String> path=data.getPathSegments();
    	
    	String type=path.get(0);
    	Intent searchIntent=new Intent();
    	searchIntent.putExtra("fromSearch", true);
    	if (path.size()>1) {
    		String dataString=path.get(1);
    		if(TextUtils.isDigitsOnly(dataString)){
    			int id=Integer.parseInt(dataString);
        		if (type.equals("courses")) {
    				searchIntent.setClass(this, CourseDetailActivity.class);
    				searchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    				searchIntent.putExtra(CourseDetailActivity.REQUEST_COURSE_KEY, id);
    				finish();
    				startActivity(searchIntent);
            	}else if (type.equals("assignments")) {
            		searchIntent.setClass(this, AssignmentDetailActivity.class);
            		searchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    				searchIntent.putExtra(Assignment.ASSUID, id);
    				finish();
    				startActivity(searchIntent);
    			}else if (type.equals("exams")) {
    				searchIntent.setClass(this, ExamDetailActivity.class);
    				searchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    				searchIntent.putExtra(Exam.EXAMID, id);
    				finish();
    				startActivity(searchIntent);
    			} 
    		}
    		//if the data is not a numeric id
    		//it means to perform an online search
    		else {
    			Uri onlineQuery=Uri.parse(dataString);
    			setContentView(R.layout.activity_search);
    			_txtSearchText = (TextView) findViewById(R.id.text);
    			_lstResults = (ListView) findViewById(R.id.list);
    			_lstResults.setCacheColorHint(Color.TRANSPARENT);
    			_queryString=onlineQuery.getPathSegments().get(1);
				_lstResults.setOnItemClickListener(this);
    			showTermDialog();
    			
			}
		}
	}
    
	private static final String[] FROM_COLS = new String[] {
			SearchManager.SUGGEST_COLUMN_TEXT_1,
			SearchManager.SUGGEST_COLUMN_TEXT_2 };
	// Specify the corresponding layout elements where we want the columns to go
	private static final int[] TO_COLS = new int[] { R.id.text1, R.id.text2 };
    
	/**
	 * The callback to process the search query and display the search result from the local database in the same activity.
	 * @param query the search query
	 */
    private void showResults(String query) {
    	_queryString=query;
    	//take the advantage of the cursor loader
        getSupportLoaderManager().initLoader(1, null, this);
        //setup the cursor adapter
         _resultAdapter = new SimpleCursorAdapter(this, R.layout.result, null, FROM_COLS, TO_COLS, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        _lstResults.setAdapter(_resultAdapter);
	}

    /**
     * The callback to process the online search and display the search result in the same activity.
     * @param searchResults the online search result
     */
    public void showSearchResults(ArrayList<Course> searchResults){
    	_searchResultCursor=searchResults;
    	if (searchResults!=null) {
			// Display the number of results
	        int count = searchResults.size();
	        String countString = getResources().getQuantityString(R.plurals.search_results,
	                                count, new Object[] {count, _queryString});
	        _txtSearchText.setText(countString);
		}else {
			_txtSearchText.setText(getString(R.string.no_results, new Object[] {_queryString}));
		}if (_searchResultCursor!=null && !_searchResultCursor.isEmpty()) {
			_searchResultAdapter =new SearchResultAdapter(this, R.layout.result, _searchResultCursor);
	        _lstResults.setAdapter(_searchResultAdapter);
		}
    }
    
	@Override
	public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
		CursorLoader cursorLoader=new CursorLoader(this, SearchProvider.CONTENT_URI, null, null, new String[]{_queryString}, null);
		return cursorLoader;
	}

	@Override
	public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
		if (cursor!=null) {
			_dbResultCursor=cursor;
			// Display the number of results
	        int count = cursor.getCount();
	        String countString = getResources().getQuantityString(R.plurals.search_results,
	                                count, new Object[] {count, _queryString});
	        _txtSearchText.setText(countString);
		}else {
			_txtSearchText.setText(getString(R.string.no_results, new Object[] {_queryString}));
		}
		
		// check the list adapter type to determine whether or not we need to
		// set a different adapter for the result list
		if (_lstResults.getAdapter() instanceof SimpleCursorAdapter) {
			((SimpleCursorAdapter)_resultAdapter).swapCursor(cursor);
		}else {
			 getSupportLoaderManager().restartLoader(1, null, this);
			_lstResults.setAdapter(_resultAdapter);
		}
		
	}
	
	@Override
	public void onLoaderReset(Loader<Cursor> arg0) {
		if (_lstResults.getAdapter() instanceof SimpleCursorAdapter) {
			((SimpleCursorAdapter)_resultAdapter).swapCursor(null);
		}else {
			 getSupportLoaderManager().restartLoader(1, null, this);
			_lstResults.setAdapter(_resultAdapter);
		}
	}
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    	getSupportMenuInflater().inflate(R.menu.search_menu, menu);
    	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            SearchManager searchManager =
                    (SearchManager) getSystemService(Context.SEARCH_SERVICE);
            android.widget.SearchView searchView =
                    ( android.widget.SearchView) menu.findItem(R.id.search).getActionView();
            searchView.setSearchableInfo(
                    searchManager.getSearchableInfo(getComponentName()));
            searchView.setIconifiedByDefault(false);
        }
    	menu.removeItem(R.id.sync);
    	return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.search:
            	if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            		onSearchRequested();
            	}
                return true;
            default:
                return false;
        }
    }
    
    public void showTermDialog() {
    	showDialogFragment(InputDialogFragment.newInstance("Select a term", R.layout.dialog_select_term,"Search"), InputDialogFragment.TAG);
	}
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
    	if (keyCode == KeyEvent.KEYCODE_SEARCH) {
    		onSearchRequested();
		}
    	return super.onKeyDown(keyCode, event);
    }
    
    /**
     * The AsyncTask to perform an online search.
     *
     */
    private static class SearchOnlineTask extends AsyncTask<String, Void, ArrayList<Course>>{
    	
    	private Context _context;
    	private String _term;
    	private ProgressDialog _progressDialog;
    	/**
    	 * The url for searching the course timetable using our dummy webservices.
    	 */
    	private static final String URL="http://utschedule.comoj.com/course_timetable.php?input=%s&term=%s";
    	public SearchOnlineTask(Context context, String term) {
    		_context=context;
    		_term=term;
		}
    	
    	@Override
    	protected void onPreExecute() {
    		super.onPreExecute();
    		_progressDialog=new ProgressDialog(_context);
    		_progressDialog.setMessage(_context.getResources().getText(R.string.loading));
    		_progressDialog.show();
    	}
    	
		@Override
		protected ArrayList<Course> doInBackground(String... query) {
			InputStream is = null;
		    try {
		    	((SearchActivity)_context).enableHttpResponseCache();
		    	String urlString=String.format(URL, query[0],_term);
		        URL url = new URL(urlString);
		        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		        conn.setReadTimeout(10000 /* milliseconds */);
		        conn.setConnectTimeout(15000 /* milliseconds */);
		        conn.setRequestMethod("GET");
		        // Starts the query
		        conn.connect();
		        int response = conn.getResponseCode();
		        //check the status code
		        if (response==HttpURLConnection.HTTP_OK) {
		        	is = new BufferedInputStream(conn.getInputStream());
		        	is.mark(1<<24);
		        	if (is.read()==-1) {
		        		if (is!=null) {
		        			is.close();
						}
						return null;
					}else {
						is.reset();
						//parse the returned json result into a list of courses
						JsonReader reader=new JsonReader(new InputStreamReader(is,"UTF-8"));
			        	ArrayList<Course> results=new ArrayList<Course>();
			        	reader.beginArray();
			        		while (reader.hasNext()) {
								reader.beginObject();
								Course c=new Course();
								while (reader.hasNext()) {
									String name=reader.nextName();
									if (name.equals("course")&&reader.peek()!=JsonToken.NULL) {
										c.set_courseCode(reader.nextString());
									}else if (name.equals("meeting")&&reader.peek()!=JsonToken.NULL) {
										c.set_section(reader.nextString());
										if (!c.get_section().contains("LEC")) {
											c.set_tutorial(true);
										}
									}else if (name.equals("starttime")&&reader.peek()!=JsonToken.NULL) {
										c.set_startTime(TimetableHelper.convertFromDateTimeStringToMillis(reader.nextString(), TimetableHelper.TYPE_TIME24).getTime());
									}else if (name.equals("endtime")&&reader.peek()!=JsonToken.NULL) {
										c.set_endTime(TimetableHelper.convertFromDateTimeStringToMillis(reader.nextString(), TimetableHelper.TYPE_TIME24).getTime());
									}else if (name.equals("location")&&reader.peek()!=JsonToken.NULL) {
										c.set_location(reader.nextString());
									}else if (name.equals("instructor")&&reader.peek()!=JsonToken.NULL) {
										c.set_instructor(reader.nextString());
									}else if (name.equals("day")&&reader.peek()!=JsonToken.NULL) {
										c.set_day(TimetableHelper.parseSearchDate(reader.nextString()));
									}else {
										reader.skipValue();
									}
								}
								reader.endObject();
								results.add(c);
							}
			        	reader.endArray();
			        	reader.close();
			        	return results;
					}
				}

		        return null;
		        
		    // Makes sure that the InputStream is closed after the app is
		    // finished using it.
		    } catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} 
			return null;
		}
		
		@Override
		protected void onPostExecute(ArrayList<Course> result) {
			_progressDialog.dismiss();
			((SearchActivity)_context).showSearchResults(result);
		}
    }

    public void enableHttpResponseCache() {
	    try {
	        long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
	        File httpCacheDir = new File(getCacheDir(), "http");
	        Class.forName("android.net.http.HttpResponseCache")
	            .getMethod("install", File.class, long.class)
	            .invoke(null, httpCacheDir, httpCacheSize);
	    } catch (Exception httpResponseCacheNotAvailable) {
	    }
	}
    
	@Override
	public void setupInputs(View v) {
	}

	@Override
	public void onInputSaveListener(View v) {
		RadioGroup radioGroup=(RadioGroup) v.findViewById(R.id.frm_radiogrp_term);
		int year=TimetableHelper.getCurrentDateCalendar().get(Calendar.YEAR);
		String term=null;
		//check the current term
		switch (radioGroup.getCheckedRadioButtonId()) {
		case R.id.frm_radio_winter:
			term=year+"1";
			break;
		case R.id.frm_radio_summer:
			term=year+"5";
			break;
		case R.id.frm_radio_fall:
			term=year+"9";
			break;
		default:
			break;
		}
		
		if (term!=null) {
			ConnectivityManager connMgr = (ConnectivityManager) 
		            getSystemService(Context.CONNECTIVITY_SERVICE);
	        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
	        //check the network state
	        if (networkInfo != null && networkInfo.isConnected()) {
	        	new SearchOnlineTask(this, term).execute(_queryString);
	        } else {
	          showDialogFragment(MessageDialogFragment.newInstance("ERROR - Network", "No network connection available."), MessageDialogFragment.TAG);
	        }
		}
		
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
		if (_lstResults.getAdapter() instanceof SimpleCursorAdapter) {
			_dbResultCursor.moveToPosition(position);
			String data=_dbResultCursor.getString(_dbResultCursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA));
			int searchQuery=_dbResultCursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
			if (searchQuery>=0) {
				
				_queryString=_dbResultCursor.getString(searchQuery);
				showTermDialog();
			}else {
				Uri dataUri=Uri.parse(data);
				gotoSearchView(dataUri);
			}
		}else {
			Course c=_searchResultAdapter.getItem(position);
			if (c!=null) {
				Intent newCourseIntent=new Intent(this,CourseFormActivity.class);
				newCourseIntent.putExtras(c.getCourseIntent());
				newCourseIntent.setAction(Intent.ACTION_VIEW);
				newCourseIntent.putExtra("RequestCode", TimetableActivity.RESULT_NEW_COURSE);
				newCourseIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
				startActivity(newCourseIntent);
				
			}
		}
	}
	
	
}
